package org.pay.service.impl;

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.pay.core.common.Exception.ServiceException;
import org.pay.core.common.constant.MchConstant;
import org.pay.core.common.constant.RetEnum;
import org.pay.core.common.util.DateUtils;
import org.pay.core.entity.MchAccount;
import org.pay.core.entity.MchAccountExample;
import org.pay.core.entity.MchAccountHistory;
import org.pay.core.service.IMchAccountService;
import org.pay.service.dao.mapper.MchAccountHistoryMapper;
import org.pay.service.dao.mapper.MchAccountMapper;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: yangshancheng
 * @date: 17/12/4
 * @description:
 */
@Service
public class MchAccountServiceImpl implements IMchAccountService {

    @Autowired
    private MchAccountMapper mchAccountMapper;

    @Autowired
    private MchAccountHistoryMapper mchAccountHistoryMapper;

    /**
     * 查询商户账户列表
     * @param offset
     * @param limit
     * @return
     */
    public List<MchAccount> listAll(int offset, int limit) {
        MchAccountExample example = new MchAccountExample();
        example.setOrderByClause("create_time asc");
        example.setLimit(limit);
        example.setOffset(offset);
        return mchAccountMapper.selectByExample(example);
    }

    /**
     * 更新用户可结算金额
     * @param mchId
     * @param totalAmount
     * @return
     */
    public int updateSettAmount(long mchId, long totalAmount) {
        Map param = new HashMap<>();
        param.put("mchId", mchId);
        param.put("totalAmount", totalAmount);
        return mchAccountMapper.updateSettAmount(param);
    }

    @Override
    public MchAccount findByMchId(Long mchId) {
        return mchAccountMapper.selectByPrimaryKey(mchId);
    }

    // 给账户加款
    @Override
    public void creditToAccount(Long mchId, Long amount, Long platProfit, Long channelCost, String requestNo, String channelOrderNo, Byte bizType) {
        MchAccount mchAccount = findByMchId(mchId);
        if(mchAccount == null) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }

        long addAmount = amount - (platProfit == null ? 0 : platProfit);

        MchAccount updateMchAccount = new MchAccount();
        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = mchAccount.getUpdateTime(); // 应该修改为账户修改时间
        if(!DateUtils.isSameDayWithToday(lastUpdateDate)) {
            updateMchAccount.setTodayExpend(0l);
            updateMchAccount.setTodayIncome(0l);
        }

        // 总收益累加和今日收益
        if (MchConstant.BIZ_TYPE_TRANSACT == bizType) {// 业务类型是交易
            updateMchAccount.setTotalIncome(mchAccount.getTotalIncome() + addAmount);

            /***** 根据上次修改时间，统计今日收益 *******/
            if (DateUtils.isSameDayWithToday(lastUpdateDate)) {
                // 如果是同一天
                updateMchAccount.setTodayIncome(mchAccount.getTodayIncome() + addAmount);
            } else {
                // 不是同一天
                updateMchAccount.setTodayIncome(addAmount);
            }
            /************************************/
        }

        // 记录资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();
        mchAccountHistory.setMchId(mchId);
        mchAccountHistory.setName(mchAccount.getName());
        mchAccountHistory.setAmount(amount);
        mchAccountHistory.setPlatProfit(platProfit);
        mchAccountHistory.setChannelCost(channelCost);
        mchAccountHistory.setBalance(mchAccount.getBalance());
        mchAccountHistory.setBizType(bizType);
        mchAccountHistory.setOrderId(requestNo);
        mchAccountHistory.setChannelOrderNo(channelOrderNo);
        mchAccountHistory.setFundDirection(MchConstant.FUND_DIRECTION_ADD);
        mchAccountHistory.setIsAllowSett(MchConstant.PUB_YES);
        mchAccountHistory.setIsCompleteSett(MchConstant.PUB_NO);
        mchAccountHistory.setRiskDay(1);

        // 账户余额
        updateMchAccount.setBalance(mchAccount.getBalance() + addAmount);
        MchAccountExample example = new MchAccountExample();
        MchAccountExample.Criteria criteria = example.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andBalanceEqualTo(mchAccount.getBalance());

        // 数据操作,保证事务
        updateAccountAmount4Transactional(mchAccountHistory, updateMchAccount, example);

    }

    // 给账户减款
    @Override
    public void debitToAccount(Long mchId, Long amount, String requestNo, String channelOrderNo, Byte bizType) {
        MchAccount mchAccount = findByMchId(mchId);
        if(mchAccount == null) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }

        Long availableBalance = mchAccount.getAvailableBalance();
        if(availableBalance.compareTo(amount) == -1) {
            // 余额不足减款
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_BALANCE_OUT_LIMIT);
        }

        MchAccount updateMchAccount = new MchAccount();

        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = mchAccount.getUpdateTime(); // 应该修改为账户修改时间
        if(!DateUtils.isSameDayWithToday(lastUpdateDate)) {
            updateMchAccount.setTodayExpend(amount);
            updateMchAccount.setTodayIncome(0l);
        }else {
            updateMchAccount.setTodayExpend(mchAccount.getTodayExpend() + amount);
        }
        updateMchAccount.setTotalExpend(mchAccount.getTotalExpend() + amount);
        // 更新后账户余额
        updateMchAccount.setBalance(mchAccount.getBalance() - amount);

        // 记录资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();
        mchAccountHistory.setMchId(mchId);
        mchAccountHistory.setName(mchAccount.getName());
        mchAccountHistory.setAmount(amount);
        mchAccountHistory.setBalance(updateMchAccount.getBalance());
        mchAccountHistory.setBizType(bizType);
        mchAccountHistory.setOrderId(requestNo);
        mchAccountHistory.setChannelOrderNo(channelOrderNo);
        mchAccountHistory.setFundDirection(MchConstant.FUND_DIRECTION_SUB);
        mchAccountHistory.setIsAllowSett(MchConstant.PUB_YES);
        mchAccountHistory.setIsCompleteSett(MchConstant.PUB_NO);
        mchAccountHistory.setRiskDay(1);

        // 账户余额
        MchAccountExample example = new MchAccountExample();
        MchAccountExample.Criteria criteria = example.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andBalanceEqualTo(mchAccount.getBalance());

        // 数据操作,保证事务
        updateAccountAmount4Transactional(mchAccountHistory, updateMchAccount, example);
    }

    // 冻结用户金额(增加用户不可用金额)
    @Override
    public void freezeAmount(Long mchId, Long freezeAmount) {
        MchAccount mchAccount = findByMchId(mchId);
        if(mchAccount == null) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }
        // 验证可用余额是否足够
        if(!mchAccount.availableBalanceIsEnough(freezeAmount)) {
            // 冻结金额超限
            throw ServiceException.build(RetEnum.RET_SERVICE_FREEZE_AMOUNT_OUT_LIMIT);
        }

        // 更新冻结金额时,保证更新时数据一致,使用update锁
        MchAccountExample example = new MchAccountExample();
        MchAccountExample.Criteria criteria = example.createCriteria();
        Long oldUnBalance = mchAccount.getUnBalance();
        BigDecimal newUnBalance = new BigDecimal(oldUnBalance).add(new BigDecimal(freezeAmount));
        criteria.andMchIdEqualTo(mchId);
        criteria.andUnBalanceEqualTo(oldUnBalance);

        mchAccount = new MchAccount();
        mchAccount.setUnBalance(newUnBalance.longValue());
        int updateCount = mchAccountMapper.updateByExampleSelective(mchAccount, example);
        if(updateCount != 1) {
            // 更新失败,抛出异常
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_FROZEN_AMOUNT_FAIL);
        }
    }

    // 解冻用金额+减款(减少用户余额,减少用户不可用余额,减少用户可结算金额)
    @Override
    public void unFreezeAmount(Long mchId, Long amount, String requestNo, Byte bizType) {
        MchAccount mchAccount = findByMchId(mchId);
        if(mchAccount == null) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }

        MchAccount updateMchAccount = new MchAccount();

        // 判断解冻金额是否超限
        if(mchAccount.getUnBalance() < amount) {
            throw ServiceException.build(RetEnum.RET_SERVICE_UN_FREEZE_AMOUNT_OUT_LIMIT);
        }

        // 记录资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();
        mchAccountHistory.setMchId(mchId);
        mchAccountHistory.setName(mchAccount.getName());
        mchAccountHistory.setAmount(amount);
        mchAccountHistory.setBalance(mchAccount.getBalance());
        mchAccountHistory.setBizType(bizType);
        mchAccountHistory.setOrderId(requestNo);
        mchAccountHistory.setChannelOrderNo(null);
        mchAccountHistory.setFundDirection(MchConstant.FUND_DIRECTION_SUB);
        mchAccountHistory.setIsAllowSett(MchConstant.PUB_NO);
        mchAccountHistory.setIsCompleteSett(MchConstant.PUB_NO);
        mchAccountHistory.setRiskDay(1);

        // 更新账户
        MchAccountExample example = new MchAccountExample();
        MchAccountExample.Criteria criteria = example.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andUnBalanceEqualTo(mchAccount.getUnBalance());
        criteria.andBalanceEqualTo(mchAccount.getBalance());
        criteria.andSettAmountEqualTo(mchAccount.getSettAmount());
        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = mchAccount.getUpdateTime(); // 应该修改为账户修改时间
        if(!DateUtils.isSameDayWithToday(lastUpdateDate)) {
            updateMchAccount.setTodayExpend(amount);
            updateMchAccount.setTodayIncome(0l);
        }else {
            updateMchAccount.setTodayExpend(mchAccount.getTodayExpend() + amount);
        }
        updateMchAccount.setBalance(mchAccount.getBalance() - amount);          // 减款
        updateMchAccount.setUnBalance(mchAccount.getUnBalance() - amount);      // 解冻
        updateMchAccount.setSettAmount(mchAccount.getSettAmount() - amount);    // 减少可结算金额

        // 数据操作,保证事务
        updateAccountAmount4Transactional(mchAccountHistory, updateMchAccount, example);

    }

    @Transactional(rollbackFor = Exception.class)
    void updateAccountAmount4Transactional(MchAccountHistory mchAccountHistory, MchAccount updateMchAccount, MchAccountExample example) {
        mchAccountHistoryMapper.insertSelective(mchAccountHistory);
        int updateCount = mchAccountMapper.updateByExampleSelective(updateMchAccount, example);
        if(updateCount != 1) {
            // 更新失败,抛出异常
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_AMOUNT_UPDATE_FAIL);
        }
    }

    // 解冻用户金额(减少用户不可用金额)
    @Override
    public void unFreezeSettAmount(Long mchId, Long amount) {
        MchAccount mchAccount = findByMchId(mchId);
        if(mchAccount == null) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }
        // 判断解冻金额是否超限
        if(mchAccount.getUnBalance() < amount) {
            throw ServiceException.build(RetEnum.RET_SERVICE_UN_FREEZE_AMOUNT_OUT_LIMIT);
        }
        MchAccount updateMchAccount = new MchAccount();
        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = mchAccount.getUpdateTime(); // 应该修改为账户修改时间
        if(!DateUtils.isSameDayWithToday(lastUpdateDate)) {
            updateMchAccount.setTodayExpend(0l);
            updateMchAccount.setTodayIncome(0l);
        }
        // 更新账户不可用金额
        MchAccountExample example = new MchAccountExample();
        MchAccountExample.Criteria criteria = example.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andUnBalanceEqualTo(mchAccount.getUnBalance());
        updateMchAccount.setUnBalance(mchAccount.getUnBalance() - amount);
        int updateCount = mchAccountMapper.updateByExampleSelective(updateMchAccount, example);
        if(updateCount != 1) {
            // 更新失败,抛出异常
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_AMOUNT_UPDATE_FAIL);
        }
    }

}
